#include "Nodes.h"
#include "Handlers.h"
#include "Node.h"
#include "HBSD.h"
#include "ConfigFile.h"

using namespace std;

Nodes::Nodes(Handlers *router) 
{
	this->router = router;
	sem_init(&nodesLock, 0, 1);
	int nodeCapacity = HBSD::routerConf->getInt(string("nodesHashCapacity"), DEFAULT_NODE_CAPACITY);
	
}

Nodes::~Nodes()
{
	while(!nodesList.empty())
	{
		map <string, Node *>::iterator iter = nodesList.begin();
		delete iter->second;
		nodesList.erase(iter);
	}
}


Node *Nodes::conditionalAdd(string eid) 
{
	// Don't create a node for ourself.
	if(eid.compare(HBSD::localEID) == 0)
	{
		return NULL;
	}
	// First check if it exists using the read lock. We may want to revisit this if we typically call this without the EID already existing, but this is probably the correct approach.

	Node *node = NULL;

	sem_wait(&nodesLock);
	node = nodesList[eid];

	// Unlock regardless of whether we found it because we'll need the write lock to create it and we can't upgrade locks.
	sem_post(&nodesLock);

	if (node != NULL) 
	{
		// Found it.
		return node;
	}

	// Go ahead and create the Node object without the lock and then acquire the write lock just to add it to the table.

	node = new Node(this, eid);
	bool callPolicy = false;
	sem_wait(&nodesLock);
	if (nodesList.find(eid) != nodesList.end()) 
	{
		// Make certain the node hadn't been created between releasing
		// the read lock and obtaining the write lock. If another thread
		// added the node while we didn't have the lock, use that object.
		node = nodesList[eid];
	} else 
	{
		nodesList[eid] = node;
		callPolicy = true;
	}
	sem_post(&nodesLock);

	if (callPolicy) 
	{
		router->policyMgr->nodeCreated(node);
		if (HBSD::log->enabled(Logging::DEBUG)) 
		{
			HBSD::log->debug(string("Added node: ") + eid);
		}
	}

	return node;
}

Node *Nodes::findNode(string eid) 
{
	Node *node = NULL;
	sem_wait(&nodesLock);
	node = nodesList[eid];
	sem_post(&nodesLock);
	return node;
}

void Nodes::allNodes(list<Node *> &listNodes) 
{
	sem_wait(&nodesLock);
	for(map <string, Node *>::iterator iter = nodesList.begin();iter != nodesList.end(); iter++)
	{
		listNodes.push_back(iter->second);
	}
	sem_post(&nodesLock);
}
